package com.opNaga.bpmp.flowObject.event;

import com.opNaga.bpmp.BPMPElement;
import com.opNaga.bpmp.connectingObject.messageFlow.MessageFlow;
import com.opNaga.bpmp.connectingObject.messageFlow.MessageFlowSource;
import com.opNaga.bpmp.connectingObject.messageFlow.MessageFlowTarget;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlow;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlowSource;
import com.opNaga.bpmp.connectingObject.sequenceFlow.SequenceFlowTarget;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * User: Junyi BEI
 * Date: 19/01/2012
 * Time: 13:58
 */
public class EndEvent extends Event implements SequenceFlowTarget, MessageFlowSource {
  private Set<BPMPElement> inwardSequenceFlows = new HashSet<BPMPElement>();
  private Set<BPMPElement> outwardMessageFlows = new HashSet<BPMPElement>();

  public EndEvent() {
    super();
  }

  public EndEvent(String t) {
    super(t);
  }

  @Override
  public String getType() {
    return "End Event";
  }

  @Override
  public boolean destroy() {
    Iterator<BPMPElement> isfIt = iterateInwardSequenceFlows();
    while(isfIt.hasNext()) {
      BPMPElement be = isfIt.next();
      if(!be.destroy()) {
        return standardMessage(801, be);
      }
    }
    Iterator<BPMPElement> omfIt = iterateOutwardMessageFlows();
    while(omfIt.hasNext()) {
      BPMPElement be = omfIt.next();
      if(!be.destroy()) {
        return standardMessage(800, be);
      }
    }
    return super.destroy();
  }

  public boolean addInwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(130, be);
    }
    if(!inwardSequenceFlows.add(be)) {
      return standardMessage(131, be);
    }
    return standardMessage(193, be);
  }

  public boolean removeInwardSequenceFlow(BPMPElement be) {
    if(!(be instanceof SequenceFlow)) {
      return standardMessage(230, be);
    }
    if(!inwardSequenceFlows.remove(be)) {
      return standardMessage(231, be);
    }
    return standardMessage(293, be);
  }

  public boolean isInwardSequenceFlowEmpty() {
    return inwardSequenceFlows.isEmpty();
  }

  public int inwardSequenceFlowSize() {
    return inwardSequenceFlows.size();
  }

  public Iterator<BPMPElement> iterateInwardSequenceFlows() {
    return new ArrayList<BPMPElement>(inwardSequenceFlows).iterator();
  }

  public boolean addOutwardMessageFlow(BPMPElement be) {
    if(!(be instanceof MessageFlow)) {
      return standardMessage(110, be);
    }
    if(!outwardMessageFlows.add(be)) {
      return standardMessage(111, be);
    }
    return standardMessage(191, be);
  }

  public boolean removeOutwardMessageFlow(BPMPElement be) {
    if(!(be instanceof MessageFlow)) {
      return standardMessage(210, be);
    }
    if(!outwardMessageFlows.remove(be)) {
      return standardMessage(211, be);
    }
    return standardMessage(291, be);
  }

  public boolean isOutwardMessageFlowEmpty() {
    return outwardMessageFlows.isEmpty();
  }

  public int outwardMessageFlowSize() {
    return outwardMessageFlows.size();
  }

  public Iterator<BPMPElement> iterateOutwardMessageFlows() {
    return new ArrayList<BPMPElement>(outwardMessageFlows).iterator();
  }

  public BPMPElement linkTo(BPMPElement be) {
    if(MessageFlowTarget.class.isAssignableFrom(be.getClass()) && ((MessageFlowTarget) be).getRoot() != root) {
      BPMPElement mf;
      if(manager != null) {
        if(be.getManager() == manager) {
          mf = manager.createInstance("mf", "-s=" + name, "-t=" + be.getName());
          if(mf != null) {
            standardMessage(910, be, mf);
            return mf;
          } else {
            standardMessage(903, be);
            return null;
          }
        } else {
          standardMessage(902, be);
          return null;
        }
      } else {
        mf = new MessageFlow();
        if(!((MessageFlow) mf).setTarget(be)) {
          mf.destroy();
          standardMessage(900, be, mf);
          return null;
        }
        if(!((MessageFlow) mf).setSource(this)) {
          mf.destroy();
          standardMessage(901, be, mf);
          return null;
        }
        if(standardMessage(911, be, mf)) {
          return mf;
        }
      }
    }
    return super.linkTo(be);
  }

  public BPMPElement getLinkTo(BPMPElement be) {
    if(MessageFlowTarget.class.isAssignableFrom(be.getClass()) && ((MessageFlowTarget) be).getRoot() != root) {
      Iterator<BPMPElement> omfIt = iterateOutwardMessageFlows();
      while(omfIt.hasNext()) {
        BPMPElement omf = omfIt.next();
        if(((MessageFlow) omf).getTarget() == be) {
          standardMessage(950, be, omf);
          return omf;
        }
      }
      standardMessage(940, be);
      return null;
    }
    return super.getLinkTo(be);
  }

  public BPMPElement linkFrom(BPMPElement be) {
    if(SequenceFlowSource.class.isAssignableFrom(be.getClass()) && ((SequenceFlowSource) be).getRoot() == root) {
      BPMPElement sf;
      if(manager != null) {
        if(be.getManager() == manager) {
          sf = manager.createInstance("sf", "-s=" + be.getName(), "-t=" + name);
          if(sf != null) {
            standardMessage(930, be, sf);
            return sf;
          } else {
            standardMessage(923, be);
            return null;
          }
        } else {
          standardMessage(922, be);
          return null;
        }
      } else {
        sf = new SequenceFlow();
        if(!((SequenceFlow) sf).setTarget(this)) {
          sf.destroy();
          standardMessage(920, be, sf);
          return null;
        }
        if(!((SequenceFlow) sf).setSource(be)) {
          sf.destroy();
          standardMessage(921, be, sf);
          return null;
        }
        if(standardMessage(931, be, sf)) {
          return sf;
        }
      }
    }
    return super.linkFrom(this);
  }

  public BPMPElement getLinkFrom(BPMPElement be) {
    if(SequenceFlowSource.class.isAssignableFrom(be.getClass()) && ((SequenceFlowSource) be).getRoot() == root) {
      Iterator<BPMPElement> isfIt = iterateInwardSequenceFlows();
      while(isfIt.hasNext()) {
        BPMPElement isf = isfIt.next();
        if(((SequenceFlow) isf).getSource() == be) {
          if(standardMessage(970, be, isf)) {
            return isf;
          } else {
            return null;
          }
        }
      }
      standardMessage(960, be);
      return null;
    }
    return super.getLinkFrom(be);
  }
}
